package org.xebia.roo.addon.jamon;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.addon.logging.LogLevel;
import org.springframework.roo.addon.logging.LoggerPackage;
import org.springframework.roo.addon.logging.LoggingOperations;
import org.springframework.roo.addon.web.flow.XmlTemplate;
import org.springframework.roo.addon.web.flow.XmlTemplate.DomElementCallback;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.Property;
import org.springframework.roo.support.util.FileCopyUtils;
import org.springframework.roo.support.util.TemplateUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Implementation of {@link JamonOperations} interface.
 *
 * @since 1.1.1
 */
@Component
@Service
public class JamonOperationsImpl implements JamonOperations {
	/**
	 * Get a reference to the FileManager from the underlying OSGi container. Make sure you
	 * are referencing the Roo bundle which contains this service in your add-on pom.xml.
	 * 
	 * Using the Roo file manager instead if java.io.File gives you automatic rollback in case
	 * an Exception is thrown.
	 */
	@Reference private FileManager fileManager;
	
	/**
	 * Get a reference to the ProjectOperations from the underlying OSGi container. Make sure you
	 * are referencing the Roo bundle which contains this service in your add-on pom.xml.
	 */
	@Reference private ProjectOperations projectOperations;
	
	@Reference private LoggingOperations loggingOperations;

	 /** {@inheritDoc} */
	public boolean isInstallJamonAvailable() {
		// enable simple performance monitoring only when it is a web project
		return projectOperations.isProjectAvailable() && fileManager.exists(projectOperations.getPathResolver().getIdentifier(Path.SRC_MAIN_WEBAPP, "/WEB-INF/web.xml"));
	}

	 /** {@inheritDoc} */
	public void installJamon() {
		
		// Parse the configuration.xml file
		Element configuration = XmlUtils.getConfiguration(getClass());

		// Add POM properties
		updatePomProperties(configuration);

		// Add dependencies to POM
		updateDependencies(configuration);
		
		
		// Use PathResolver to get canonical resource names for a given artifact
		PathResolver pathResolver = projectOperations.getProjectMetadata().getPathResolver();
		String resolvedSpringConfigPath = pathResolver.getIdentifier(Path.SRC_MAIN_WEBAPP, "/WEB-INF/spring");
		if (fileManager.exists(resolvedSpringConfigPath + "/web-jamon-config.xml")) {
			return;
		}
		copyTemplate("web-jamon-config.xml", resolvedSpringConfigPath);
		
		String webMvcConfigPath = resolvedSpringConfigPath + "/webmvc-config.xml";

		new XmlTemplate(fileManager).update(webMvcConfigPath, new DomElementCallback() {
			public boolean doWithElement(Document document, Element root) {
				if (null == XmlUtils.findFirstElement("/beans/import[@resource='web-jamon-config.xml']", root)) {
					Element element = document.createElement("import");
					element.setAttribute("resource", "web-jamon-config.xml");
					root.appendChild(element);
					return true;
				}
				return false;
			}
		});
		loggingOperations.configureLogging(LogLevel.TRACE, LoggerPackage.PROJECT);
		
	}
	
	private void copyTemplate(String templateFileName, String resolvedTargetDirectoryPath) {
		try {
			FileCopyUtils.copy(TemplateUtils.getTemplate(getClass(), templateFileName), fileManager.createFile(resolvedTargetDirectoryPath + "/" + templateFileName).getOutputStream());
		} catch (IOException e) {
			throw new IllegalStateException("Encountered an error during copying of resources for Jamon addon.", e);
		}
	}
	
	private void updatePomProperties(Element configuration) {
		List<Element> properties = XmlUtils.findElements("/configuration/jamon/properties/*", configuration);
		for (Element property : properties) {
			projectOperations.addProperty(new Property(property));
		}
	}

	private void updateDependencies(Element configuration) {
		List<Dependency> dependencies = new ArrayList<Dependency>();
		List<Element> jamonDependencies = XmlUtils.findElements("/configuration/jamon/dependencies/dependency", configuration);
		for (Element dependencyElement : jamonDependencies) {
			dependencies.add(new Dependency(dependencyElement));
		}
		projectOperations.addDependencies(dependencies);
	}
	
}